package xyz.lwm.lazycat.utility.metaobject;

import org.jetbrains.annotations.NotNull;
import xyz.lwm.lazycat.utility.Assert;
import xyz.lwm.lazycat.utility.It;
import xyz.lwm.lazycat.utility.convert.Types;
import xyz.lwm.lazycat.utility.reflect.ReflectUtil;

import java.util.HashMap;
import java.util.Map;

/**
 * @author lwm
 */
public abstract class MetaObjectUtil {

    /**
     * 获取元对象
     *
     * @param obj    对象
     * @param config 配置
     * @return 元对象
     */
    @SuppressWarnings("unchecked")
    @NotNull
    public static MetaObject getMetaObject(Object obj, MetaConfig config) {
        Assert.isTrue(obj != null && Types.isBeanType(obj.getClass()),
                "obj is null or not bean type");
        return obj instanceof Map ? new MapObject((Map<String, Object>) obj) : new BeanObject(obj, config);
    }

    /**
     * 复制属性值, 使用默认配置
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        copyProperties(source, target, MetaConfig.create());
    }

    /**
     * 复制属性值
     *
     * @param source 源对象
     * @param target 目标对象
     * @param config 配置
     */
    public static void copyProperties(Object source, Object target, MetaConfig config) {

        if (source == null || target == null ||
                !Types.isBeanType(target.getClass()) ||
                !Types.isBeanType(source.getClass())) {
            return;
        }

        MetaObject sourceMeta = getMetaObject(source, config);
        MetaObject targetMeta = getMetaObject(target, config);

        for (String propertyName : sourceMeta.findPropertyNames()) {
            if (!config.isIgnoreProperty(propertyName)) {
                Object value = sourceMeta.getValue(propertyName);
                if (value != null || !config.isIgnoreNull()) {
                    targetMeta.setValue(config.getTargetPropertyName(propertyName), value);
                }
            }
        }

    }

    /**
     * 创建目标对象并复制属性值, 使用默认配置
     *
     * @param source 源对象
     * @param type 目标类型
     */
    public static <T> T copyProperties(Object source, Class<T> type) {
        return copyProperties(source, type, MetaConfig.create());
    }

    /**
     * 创建目标对象并复制属性值
     *
     * @param source 源对象
     * @param type 目标类型
     * @param config 配置
     */
    @SuppressWarnings("unchecked")
    public static <T> T copyProperties(Object source, Class<T> type, MetaConfig config) {
        Assert.isTrue(type != null && Types.isBeanType(type), "Invalid parameter");
        Class<T> clazz = It.of(type).filter(Map.class::isAssignableFrom)
                .filter(clz -> Map.class == clz).let(clz -> HashMap.class)
                .let(clz -> (Class<T>) clz).getOr(type);
        return ReflectUtil.newInstance(clazz, target -> copyProperties(source, target, config));
    }


}
